/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/**
 * @author WORKSPOT2
 */
public class Percolation {
    private boolean[][] grade;
    private WeightedQuickUnionUF wqu;
    private WeightedQuickUnionUF wquSecond;

    public Percolation(int N) {
        grade = new boolean[N][N];
        wqu = new WeightedQuickUnionUF(N * N + 1 + 1);
        wquSecond = new WeightedQuickUnionUF(N * N + 1);

    }

    private int getCounterByCoords(int k, int l) {
        int counter = k * grade.length + l + 1;
        return counter;
    }

    public void open(int i, int j) {
        int normalI = i - 1;
        int normalJ = j - 1;
        boolean isOpen = grade[normalI][normalJ];
        if (!isOpen) {
            grade[normalI][normalJ] = true;
            unionNeighbours(normalI, normalJ);
        }
    }

    private void unionNeighbours(int i, int j) {
        int currentValue = getCounterByCoords(i, j);
        doOneUnion(currentValue, i - 1, j);
        doOneUnion(currentValue, i + 1, j);
        doOneUnion(currentValue, i, j - 1);
        doOneUnion(currentValue, i, j + 1);
    }

    private void doOneUnion(int currentValue, int i, int j) {

        if (i == -1) {
            wqu.union(currentValue, 0);
            wquSecond.union(currentValue, 0);
        } else if (i == grade.length) {
            wqu.union(currentValue, grade.length * grade.length + 1);
        } else {
            int unionValue = getValueForUnion(i, j);
            if (unionValue > 0) {
                wqu.union(currentValue, unionValue);
                wquSecond.union(currentValue, unionValue);
            }
        }
    }

    private int getValueForUnion(int i, int j) {
        boolean check = checkIndexes(i, j);
        if (check) {
            if (grade[i][j]) {
                return getCounterByCoords(i, j);
            }
        }
        return -1;
    }

    public boolean isOpen(int i, int j) {
        int normalI = i - 1;
        int normalJ = j - 1;
        boolean indexCheck = checkIndexes(normalI, normalJ);
        if (!indexCheck) {
            throw new IndexOutOfBoundsException();
        }
        return grade[normalI][normalJ];
    }

    public boolean isFull(int i, int j) {
        int normalI = i - 1;
        int normalJ = j - 1;
        boolean indexCheck = checkIndexes(normalI, normalJ);
        if (!indexCheck) {
            throw new IndexOutOfBoundsException();
        }
        boolean isOpen = grade[normalI][normalJ];
        int value = getCounterByCoords(normalI, normalJ);
        return isOpen && wquSecond.connected(0, value);
    }

    public boolean percolates() {
        return wqu.connected(0, grade.length * grade.length + 1);
    }

    private boolean checkIndexes(int i, int j) {
        return (i >= 0 && i < grade.length) && (j >= 0 && j < grade.length);
    }
}
